package sketch.specs;

import java.util.LinkedList;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.internal.core.dom.NaiveASTFlattener;

import sketch.ast.ASTUtils;
import sketch.ounit.Values;
import sketch.util.Checker;

public class SpecValueDeclarationRemover extends NaiveASTFlattener {
	
	public List<VariableDeclarationStatement> toberemoved = new LinkedList<VariableDeclarationStatement>();

	public boolean visit(VariableDeclarationStatement var_decl) {
		if(isSpecValueDeclarationStatement(var_decl)) {
			
//			ASTNode parent = var_decl.getParent();
//			Checker.checkTrue(parent instanceof Block, "The spec value declaration statement must be used inside a block");
//			Block block = (Block)parent;
//			//remove the var declaration
//			block.statements().remove(var_decl);
			
			toberemoved.add(var_decl);
			
			return false;
		}
		return  super.visit(var_decl);
	}
	
	public void remove_var_decls() {
		for(VariableDeclarationStatement var_decl : toberemoved) {
			ASTNode parent = var_decl.getParent();
			Checker.checkTrue(parent instanceof Block, "The spec value declaration statement must be used inside a block");
			Block block = (Block)parent;
			//remove the var declaration
			block.statements().remove(var_decl);
		}
	}
	
//	public static void main(String[] args) {
//		CompilationUnit unit = ASTUtils.astRoot("./tests/sketch/issta11/specifyvalues/Z_ChooseOneValue_Example.java");
//		SpecValueDeclarationRemover remover = new SpecValueDeclarationRemover();
//		unit.accept(remover);
//		System.out.println(unit);
//	}
	
	public static String[] method_invoke_exprs = new String[]{"Values", "sketch.ounit.Values"};
	public static String[] method_invoke_names = new String[]{Values.choose, Values.subset, Values.exhaust/*, "randomChoose", "chooseArray"*/};
	
	public static boolean isSpecValueDeclarationStatement(VariableDeclarationStatement var_decl) {
		return isSpecValueDeclarationStatement(var_decl, method_invoke_exprs, method_invoke_names);
	}
	
	public static boolean isSpecValueDeclarationStatement(VariableDeclarationStatement var_decl, String[] method_invoke_exprs, String[] method_invoke_names) {
		List<VariableDeclarationFragment> fragments = var_decl.fragments();
		if(fragments.size() == 1) {
			VariableDeclarationFragment fragment = fragments.get(0);
			return isFragmentValueDeclaration(fragment, method_invoke_exprs, method_invoke_names);
		} else {
			for(VariableDeclarationFragment fragment : fragments) {
				Checker.checkTrue(!isFragmentValueDeclaration(fragment, method_invoke_exprs, method_invoke_names), "Could not use Values.choose method nestedly.");
			}
			return false;
		}
	}
	
	private static boolean isFragmentValueDeclaration(VariableDeclarationFragment fragment, String[] method_invoke_exprs, String[] method_invoke_names) {
		Expression expr = fragment.getInitializer();
		if(expr instanceof MethodInvocation) {
			MethodInvocation invoke = (MethodInvocation)expr;
			boolean expr_correct = false;
			boolean name_correct = false;
			for(String method_expr : method_invoke_exprs) {
//				System.out.println(method_expr + ",  invoke: " + invoke);
				if(invoke.getExpression() == null) {
					continue;
				}
				if(invoke.getExpression().toString().equals(method_expr)) {
					expr_correct = true;
					break;
				}
			}
			for(String method_name : method_invoke_names) {
				if(invoke.getName().toString().equals(method_name)) {
					name_correct = true;
					break;
				}
			}
			return expr_correct && name_correct;
		} else {
			return false;
		}
	}
}